package com.example.socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @Date 2022/10/15
 * @Author Jonathan
 */
public class ServerDemo {

    private static final Logger log = LoggerFactory.getLogger(ServerDemo.class);

    private static final String XML_HEADER = "<?xml version=\"1.0\"encoding=\"GBK\"?>";
    private static final String XML_CONTENT = "<hnnx_cydbd><trancode>现在是2023年1月4日17:25:28</trancode><bizToken>425CB547-74F4-4912-BEB2-DE3FFBD74805</bizToken><appid>xxxxxxxxxxxxxxxxxx</appid><ruleid>2</ruleid></hnnx_cydbd>";


    public static void main(String[] args) throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false); // 设置为非阻塞
        serverSocketChannel.bind(new InetSocketAddress(8000)); // 设置端口

        // 多路复用器
        Selector selector = Selector.open();
        // 注册到多路复用器 并且关注连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next(); // 这个key既包含sc也包含ssc 但是ssc是全局唯一的
                iterator.remove();
                if (key.isAcceptable()) {
                    // ssc 就去接待进来  接待进来的再设置为非阻塞  阻塞到多路复用器上面
                    // 注册channel得到的句柄 可以为该channel添加附件
                    SocketChannel channel = serverSocketChannel.accept();
                    channel.configureBlocking(false);   // 设置为非阻塞
                    SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);
                    // 向这里面写数据
//                    StringBuilder builder = new StringBuilder();
//                    for (int i = 0; i < 200_0000; i++) {
//                        builder.append("s");
//                    }
                    String content = XML_HEADER + XML_CONTENT;
                    content = String.format("%08d", content.length()) + content;
                    log.info("writeContent:{}", content);

                    ByteBuffer byteBuffer = Charset.forName("GB2312").encode(content);

                    int write = channel.write(byteBuffer);
                    log.info("write:{}", write);
                    // 写一次之后 添加写事件  然后等可以写了 在写
                    if (byteBuffer.hasRemaining()) {
                        //todo   添加写事件 添加附件
                        selectionKey.attach(byteBuffer);
                        selectionKey.interestOps(selectionKey.interestOps() + SelectionKey.OP_WRITE);
                    }
                    //key.attach(byteBuffer);
                } else if (key.isWritable()) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    int write = channel.write(buffer);
                    log.info("write:{}", write);
                    if (!buffer.hasRemaining()) {
                        // buffer里面没有之后 就移除事件  和附件
                        key.attach(null);
                        key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
                    }
                } else if (key.isReadable()) {

                    int headerSize = 8;
                    int alreadyReadHeader = 0;
                    ByteBuffer headerBuffer = ByteBuffer.allocate(headerSize);
                    SocketChannel channel = (SocketChannel) key.channel();
                    boolean disconnected = false;
                    do {
                        int read = channel.read(headerBuffer);
                        if (read == -1) {
                            disconnected = true;
                            key.cancel();
                            break;
                        }
                        log.info("read:{}", read);
                        alreadyReadHeader += read;
                    } while (alreadyReadHeader != headerSize);
                    headerBuffer.flip();
                    //CharBuffer charBuffer = StandardCharsets.UTF_8.decode(headerBuffer);
                    CharBuffer charBuffer = Charset.forName("GB2312").decode(headerBuffer);
                    headerBuffer.clear();

                    // 如果客户端已经断开就不执行后面的代码
                    if (disconnected) {
                        continue;
                    }

                    int contentLength = Integer.parseInt(charBuffer.toString());
                    int alreadyReadContent = 0;
                    ByteBuffer contentBuffer = ByteBuffer.allocate(contentLength);
                    do {
                        int read = channel.read(contentBuffer);
                        if (read == -1) {
                            key.cancel();
                            break;
                        }
                        alreadyReadContent += read;
                        log.info("read:{}", read);
                    } while (alreadyReadContent != contentLength);
                    contentBuffer.flip();
                    CharBuffer content = Charset.forName("GB2312").decode(contentBuffer);
                    log.info("receivedContent:{}", content);

                }
            }
        }
    }
}
